%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Module summary
%%
%% Main authors: Olle, Kristian
%% Progress to usefulness: 80%
%%
%% Library functions for communication between client and database.
%% Made especially for the server side, but could be useful for an 
%% Erlang client as well, if extended a bit. 
%%
%% Simple binary protocol with a message header, variable length fields.
%% Build on top of TCP. 
%%
%%%%%%%%%%%%%%%%%%%%%%%

-module(clientiface).
-compile(export_all).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                                     %%
%%  Message: <Header>
%%           <Key field>
%%           <Value field>
%%
%%  Header: <Command> <keylen> <valuelen>
%%
%%  keylen,valuelen: unsigned 32-bit integer
%%
%%  put: PUTaaaabbbb<key><value>
%%          ok reply is POKaaaa0000<key>
%%          error reply is PERaaaa0000<key>
%%
%%  get: GETaaaa0000<key>
%%          ok reply is GOKaaaabbbb<key><value>
%%          error reply is GERaaaa0000<key>
%%
%%  remove: DELaaaa0000<key>
%%          ok reply is DOKaaaa0000<key>
%%          error reply is DERaaaa0000<key>
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%  Input
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Read and parse a message header directly from a socket.
get_header(Socket) ->
   Code = networking:get_bytes_from_socket(Socket,3),
   KeyLen = networking:get_uint_from_socket(Socket,4),
   ValueLen = networking:get_uint_from_socket(Socket,4),
   { Code, KeyLen, ValueLen }.

%% Read and parse a whole message directly from the socket. 
get_message(Socket) ->
   Header = get_header(Socket),
   { Code, KeyLen, ValueLen } = Header,
   Key = networking:get_bytes_from_socket(Socket, KeyLen),
   Value = networking:get_bytes_from_socket(Socket, ValueLen),
   { Code, Key, Value }.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%  Output
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Build a sequence of bytes containing a message header,
%% according to parameters.
make_header(Code, KeyLen, ValueLen) ->
   Code ++ encode_int_to_list(KeyLen) ++ encode_int_to_list(ValueLen).

encode_int_to_list(Integer) ->
    Encoded = binary_to_list(binary:encode_unsigned(Integer,big)),
    case length(Encoded) < 4 of
        false ->  Encoded;
        true  -> [0||_<-lists:seq(1,4-length(Encoded))] ++ Encoded
    end.
    
%% Build a sequence of bytes containing a whole message,
%% according to parameters. 
make_message(Code, Key, Value) ->
   KeyLen = length(Key),
   ValueLen = length(Value),
   Header = make_header(Code, KeyLen, ValueLen),
   Header ++ Key ++ Value.

%% Build a message according to parameters, 
%% and send it to the socket. 
make_and_send(Socket, Code, Key, Value) ->
   Msg = make_message(Code, Key, Value),
   gen_tcp:send(Socket, Msg).

%% Overloaded function WOHOOO!!!!!!!!!!!
make_and_send(Socket, Code, Key) ->
   Msg = make_message(Code, Key, ""),
   gen_tcp:send(Socket, Msg).


reply_put_ok(Socket, Key) ->
   make_and_send(Socket, "POK", Key).

reply_put_error(Socket, Key) ->
   make_and_send(Socket, "PER", Key).

reply_get_ok(Socket, Key, Value) ->
   make_and_send(Socket, "GOK", Key, Value).

reply_get_error(Socket, Key) ->
   make_and_send(Socket, "GER", Key).

reply_remove_ok(Socket, Key) ->
   make_and_send(Socket, "DOK", Key).

reply_remove_error(Socket, Key) ->
   make_and_send(Socket, "DER", Key).

